use std::fmt;

#[derive(PartialEq, Eq, Clone)]
pub enum Attribute {
    AlignStack(u64),
    AllocSize {
        elt_size: u32,
        num_elts: Option<u32>,
    },
    AlwaysInline,
    Builtin,
    Cold,
    Convergent,
    InaccessibleMemOnly,
    InaccessibleMemOrArgMemOnly,
    InlineHint,
    JumpTable,
    MinimizeSize,
    MustProgress,
    Naked,
    NoBuiltin,
    NoCFCheck,
    NoDuplicate,
    NoFree,
    NoImplicitFloat,
    NoInline,
    NonLazyBind,
    NoRedZone,
    NoReturn,
    NoRecurse,
    WillReturn,
    ReturnsTwice,
    NoSync,
    NoUnwind,
    OptForFuzzing,
    OptNone,
    OptSize,
    ReadNone,
    ReadOnly,
    WriteOnly,
    ArgMemOnly,
    SafeStack,
    SanitizeAddress,
    SanitizeMemory,
    SanitizeThread,
    SanitizeHWAddress,
    SanitizeMemTag,
    ShadowCallStack,
    SpeculativeLoadHardening,
    Speculatable,
    StackProtect,
    StackProtectReq,
    StackProtectStrong,
    StrictFP,
    UWTable,
    StringAttribute {
        kind: String,
        value: String,
    },
    Ref(u32),
    // UnknownAttribute,
}

impl fmt::Debug for Attribute {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Attribute::AlwaysInline => write!(f, "alwaysinline"),
            Attribute::Builtin => write!(f, "builtin"),
            Attribute::Cold => write!(f, "cold"),
            Attribute::Convergent => write!(f, "convergent"),
            Attribute::InaccessibleMemOnly => write!(f, "inaccessiblememonly"),
            Attribute::InaccessibleMemOrArgMemOnly => write!(f, "inaccessiblememorargmemonly"),
            Attribute::InlineHint => write!(f, "inlinehint"),
            Attribute::JumpTable => write!(f, "jumptable"),
            Attribute::MinimizeSize => write!(f, "minimizesize"),
            Attribute::MustProgress => write!(f, "mustprogress"),
            Attribute::Naked => write!(f, "naked"),
            Attribute::NoBuiltin => write!(f, "nobuiltin"),
            Attribute::NoCFCheck => write!(f, "nocfcheck"),
            Attribute::NoDuplicate => write!(f, "noduplicate"),
            Attribute::NoFree => write!(f, "nofree"),
            Attribute::NoImplicitFloat => write!(f, "noimplicitfloat"),
            Attribute::NoInline => write!(f, "noinline"),
            Attribute::NonLazyBind => write!(f, "nonlazybind"),
            Attribute::NoRedZone => write!(f, "noredzone"),
            Attribute::NoReturn => write!(f, "noreturn"),
            Attribute::NoRecurse => write!(f, "norecurse"),
            Attribute::WillReturn => write!(f, "willreturn"),
            Attribute::ReturnsTwice => write!(f, "returnstwice"),
            Attribute::NoSync => write!(f, "nosync"),
            Attribute::NoUnwind => write!(f, "nounwind"),
            Attribute::OptForFuzzing => write!(f, "optforfuzzing"),
            Attribute::OptNone => write!(f, "optnone"),
            Attribute::OptSize => write!(f, "optsize"),
            Attribute::ReadNone => write!(f, "readnone"),
            Attribute::ReadOnly => write!(f, "readonly"),
            Attribute::WriteOnly => write!(f, "writeonly"),
            Attribute::ArgMemOnly => write!(f, "argmemonly"),
            Attribute::SafeStack => write!(f, "safestack"),
            Attribute::SanitizeAddress => write!(f, "sanitizeaddress"),
            Attribute::SanitizeMemory => write!(f, "sanitizememory"),
            Attribute::SanitizeThread => write!(f, "sanitizethread"),
            Attribute::SanitizeHWAddress => write!(f, "sanitizehwaddress"),
            Attribute::SanitizeMemTag => write!(f, "sanitizememtag"),
            Attribute::ShadowCallStack => write!(f, "shadowcallstack"),
            Attribute::SpeculativeLoadHardening => write!(f, "speculativeloadhardening"),
            Attribute::Speculatable => write!(f, "speculatable"),
            Attribute::StackProtect => write!(f, "ssp"),
            Attribute::StackProtectReq => write!(f, "sspreq"),
            Attribute::StackProtectStrong => write!(f, "sspstrong"),
            Attribute::StrictFP => write!(f, "strictfp"),
            Attribute::UWTable => write!(f, "uwtable"),
            Attribute::StringAttribute { kind, value } if value.is_empty() => {
                write!(f, "\"{}\"", kind)
            }
            Attribute::StringAttribute { kind, value } => write!(f, "\"{}\"=\"{}\"", kind, value),
            Attribute::Ref(i) => write!(f, "#{}", i),
            _ => todo!(),
        }
    }
}
